#ifdef _DEBUG_
#include <iostream>
using namespace std;
#endif

#include <unistd.h>

#include "edaParWrapperControl.h"
#include "edaParSearchWrapper.ph"

edaParWrapperControl::edaParWrapperControl() :
  edgeNum (0), vertexNum (0)
{
}

int edaParWrapperControl::insertVertex(edaSearch *sa)
{
  int id = vertexNum;
  vertexNum++;

  taskDAG.insertVertex(id, sa);
  taskStatus[id] = STATUS_PENDING;

  return id;
}

int edaParWrapperControl::insertEdge(const int from, const int to)
{
  int id = edgeNum;
  edgeNum++;

  taskDAG.insertEdge(id, from, to);

  printDebug(4, "parent of [" << to << "] is [" << from <<"]");

  return id;
}

vector<int> edaParWrapperControl::findReadyNodes()
{
  vector<int> nodeList;
  vector<int> readyNodes;
  vector<int>::iterator intIter;

  nodeList = taskDAG.traverse();
  for (intIter = nodeList.begin(); intIter != nodeList.end(); intIter++)
  {
    // Check if the node is not in PENDING state
    map<int, int>::const_iterator mci = taskStatus.find(*intIter);
    if (mci->second != STATUS_PENDING)
    {
      // Then bypass
      continue;
    }

    // Find parents node
    vector<int> parentNodes;
    parentNodes = taskDAG.getParentNodes(*intIter);

    // Check parent nodes that all nodes is FINISHED
    vector<int>::iterator parentIter;
    bool allFinished = true;
    for (parentIter = parentNodes.begin(); parentIter != parentNodes.end(); parentIter++)
    {
      mci = taskStatus.find(*parentIter);
      if (mci->second != STATUS_FINISHED)
      {
        allFinished = false;
        break;
      }
    }

    if (allFinished)
    {
      readyNodes.push_back(*intIter);
    }
  }

  return readyNodes;
}

bool edaParWrapperControl::allDone()
{
  vector<int> nodeList;
  vector<int>::iterator intIter;
  nodeList = taskDAG.traverse();

  for (intIter = nodeList.begin(); intIter != nodeList.end(); intIter++)
  {
    map<int, int>::const_iterator mci = taskStatus.find(*intIter);

    if (mci->second != STATUS_FINISHED)
    {
      printDebug(5, "not done: " << mci->first);
      return false;
    }
  }

  return true;
}

bool edaParWrapperControl::search(edaSolution &inputSol)
{
  map<int, edaSolution*> taskSolution;
  map<int, edaParSearchWrapper*> taskWrapper;
  int lastSearch = -1;

  while (!allDone())
  {
    vector<int> readyNodes = findReadyNodes();

    vector<int>::iterator readyIter;

    // Do the search on all ready nodes
    for (readyIter = readyNodes.begin(); readyIter != readyNodes.end(); readyIter++)
    {
      printDebug(3, "NodeID: " << *readyIter);

      edaSolution *mysol = NULL;
      edaSearch *&sa = taskDAG[*readyIter];
      lastSearch = *readyIter;

      // Get parents of this search
      vector<int> parentNodes;
      parentNodes = taskDAG.getParentNodes(*readyIter);

      if (parentNodes.empty())
      {
        // If no parent, use input solution
        mysol = inputSol.clone();
      }
      else
      {
        // If there is parents, use the best solution of them
        double bestFitness = -1e100;
        int bestSolution;
        vector<int>::iterator parentIter;

        for (parentIter = parentNodes.begin(); 
            parentIter != parentNodes.end(); 
            parentIter++)
        {
          double f = (taskSolution[*parentIter])->fullEvaluate();
          if (bestFitness < f)
          {
            bestFitness = f;
            bestSolution = *parentIter;
          }
        }

        mysol = taskSolution[bestSolution]->clone();
      }

      printDebug(4, "solution after clone: " << mysol);

      // Prepare buffer for search algorithm
      edaBuffer sa_buf;
      sa->doSerialize(sa_buf, true);

      // Create Parallel Wrapper
      edaParSearchWrapper *sw = new edaParSearchWrapper(sa_buf);
      taskWrapper[*readyIter] = sw;

      printDebug(4, "TaskID: " << *readyIter << ", &sw = " << (int)sw);

      // Prepare buffer for solution
      edaBuffer sol_buf;
      mysol->doSerialize(sol_buf, true);

      taskStatus[*readyIter] = STATUS_RUNNING;
      // Call the search
      sw->search(sol_buf);
      printDebug(5, "Searching in progress...");

      int tmpStatus;
      sw->getCurrentStatus(tmpStatus);

      if (mysol != NULL)
      {
        delete mysol;
      }
    }

    // Polling for finished
    int jobFinished = polling(taskWrapper);
    printDebug(3, "Job finished: " << jobFinished);
    if (jobFinished != -1)
    {
      taskStatus[jobFinished] = STATUS_FINISHED;

      // Get the solution
      edaBuffer result_buf;
      edaParSearchWrapper *sw = taskWrapper[jobFinished];

      sw->getSolution(result_buf);
      edaSolution *result = (edaSolution*)classGenerateFromBuffer(result_buf);
      taskSolution[jobFinished] = result;

      // Delete wrapper
      delete sw;
      taskWrapper[jobFinished] = NULL;
    }
  }

  inputSol = *(taskSolution[lastSearch]);

  // Delete solution map
  map<int, edaSolution*>::iterator mapIter;
  for (mapIter = taskSolution.begin(); mapIter != taskSolution.end(); mapIter++)
  {
    delete mapIter->second;
  }

  return true;
}

int edaParWrapperControl::polling(map<int, edaParSearchWrapper*> &taskWrapper)
{
  while (true)
  {
    map<int, edaParSearchWrapper*>::iterator mapIter;

    printDebug(5, "Polling");
    for (mapIter = taskWrapper.begin(); mapIter != taskWrapper.end(); mapIter++)
    {
      // Poll only running task
      if (taskStatus[mapIter->first] != STATUS_RUNNING)
      {
        continue;
      }

      edaParSearchWrapper *sw = mapIter->second;

      printDebug(5, "Search wrapper pointer: " << (int) sw);
      int swStatus;
      sw->getCurrentStatus(swStatus);
      int swID = mapIter->first;

      printDebug(4, "TaskID: " << mapIter->first << ", &sw = " << (int)sw << ", status = " << swStatus);
      if (swStatus != taskStatus[swID])
      {
        return swID;
      }
    }

    sleep(SLEEP_TIME);
  }
}
